package com.sawyerqiu.ui.plugin;

import javax.swing.*;
import javax.swing.plaf.basic.BasicArrowButton;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * 下拉复选框组件
 */
public class MultiComboBox<E> extends JComponent implements ActionListener {

    private Object[] values;
    private MultiPopup popup;
    private JTextField editor;
    protected JButton arrowButton;

    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    private ScheduledFuture<?> scheduledFuture;

    public MultiComboBox() {
        values = new Object[0];
        initComponent();

        editor.setText("选择xlsx文件以筛选数据");
    }

    public void setDatum(E[] datum) {
        values = datum;
        initComponent();

        editor.setText("选择导出的数据（不选则全部）");
    }

    private void initComponent() {
        this.setLayout(new BorderLayout());
        popup = new MultiPopup(values);
        editor = new JTextField();
        editor.setBackground(Color.WHITE);
        editor.setEditable(false);
        editor.setPreferredSize(new Dimension(200, 22));
        editor.addActionListener(this);
        arrowButton = createArrowButton();
        arrowButton.addActionListener(this);
        add(editor, BorderLayout.WEST);
        add(arrowButton, BorderLayout.CENTER);
    }

    public void walkSelectedValues(Consumer<E> consumer) {
        Object[] selectedValues = popup.getSelectedValues();
        for (Object selectedValue : selectedValues) {
            consumer.accept((E) selectedValue);
        }
    }

    //设置需要选中的值
    public void setSelectValues(E[] selectvalues) {
        popup.setSelectValues(selectvalues);
        setText(selectvalues);
    }

    private void setText(Object[] values) {
        if (values.length > 0) {
            String value = Arrays.toString(values);
            value = value.replace("[", "");
            value = value.replace("]", "");
            editor.setText(value);
        } else {
            editor.setText("");
        }
    }

    @Override
    public void actionPerformed(ActionEvent arg0) {
        // TODO Auto-generated method stub
        if (values.length == 0) {
            return;
        }

        if (!popup.isVisible()) {
            popup.show(this, 0, getHeight());
        }
    }

    protected JButton createArrowButton() {
        JButton button = new BasicArrowButton(BasicArrowButton.SOUTH, UIManager.getColor("ComboBox.buttonBackground"),
                UIManager.getColor("ComboBox.buttonShadow"), UIManager.getColor("ComboBox.buttonDarkShadow"),
                UIManager.getColor("ComboBox.buttonHighlight"));
        button.setName("ComboBox.arrowButton");
        return button;
    }


    //内部类MultiPopup

    public class MultiPopup extends JPopupMenu implements ActionListener {
        private Object[] values;
        private List<JCheckBox> checkBoxList = new ArrayList<JCheckBox>();
        private JButton commitButton;
        private JButton cancelButton;

        public MultiPopup(Object[] value) {
            super();
            values = value;
            initComponent();
        }

        private void initComponent() {
            this.setLayout(new BorderLayout());

            JPanel searchPanel = new JPanel();

            JPanel checkboxPane = new JPanel();
            checkboxPane.setLayout(new GridLayout(0, 1));

            JPanel buttonPane = new JPanel();

            JTextField searchTextField = new JTextField();
            searchTextField.setPreferredSize(new Dimension(199, 30));
            searchTextField.addKeyListener(new KeyAdapter() {
                @Override
                public void keyReleased(KeyEvent e) {
                    super.keyReleased(e);
                    if (scheduledFuture != null) {
                        scheduledFuture.cancel(true);
                    }

                    scheduledFuture = executor.schedule(() -> {
                        checkboxPane.removeAll();
                        String text = searchTextField.getText();
                        if ("".equals(text)) {
                            refreshCheckBoxPanel(checkboxPane, box -> true);

                        } else {
                            refreshCheckBoxPanel(checkboxPane, box -> haveSameCharacters(text, box.getText()));
                        }

                        checkboxPane.repaint();

                    }, 1000, TimeUnit.MILLISECONDS);

                }
            });

            searchPanel.add(searchTextField);

            for (Object v : values) {
                JCheckBox temp = new JCheckBox(v.toString());
                checkBoxList.add(temp);
            }

            refreshCheckBoxPanel(checkboxPane, box -> true);

            commitButton = new JButton("确定");
            commitButton.addActionListener(this);

            cancelButton = new JButton("取消");
            cancelButton.addActionListener(this);

            buttonPane.add(commitButton);
            buttonPane.add(cancelButton);

            JScrollPane jScrollPane = new JScrollPane(checkboxPane);
            jScrollPane.setPreferredSize(new Dimension(200, 500));

            this.add(searchPanel, BorderLayout.NORTH);
            this.add(jScrollPane, BorderLayout.CENTER);
            this.add(buttonPane, BorderLayout.SOUTH);

        }

        private void refreshCheckBoxPanel(JPanel checkboxPane, Predicate<JCheckBox> jCheckBoxPredicate) {
            for (JCheckBox box : checkBoxList) {
                if (!jCheckBoxPredicate.test(box)) {
                    continue;
                }

                checkboxPane.add(box);
            }
        }

        public void setSelectValues(E[] values) {
            if (values.length > 0) {
                for (int i = 0; i < values.length; i++) {
                    for (int j = 0; j < checkBoxList.size(); j++) {
                        if (values[i].equals(checkBoxList.get(j).getText())) {
                            checkBoxList.get(j).setSelected(true);
                        }
                    }
                }
                setText(getSelectedValues());
            }
        }


        public Object[] getSelectedValues() {
            List<Object> selectedValues = new ArrayList<Object>();

            if (checkBoxList.get(0).getText().equals("全选")) {
                if (checkBoxList.get(0).isSelected()) {
                    for (int i = 1; i < checkBoxList.size(); i++) {
                        selectedValues.add(values[i]);
                    }
                } else {
                    for (int i = 1; i < checkBoxList.size(); i++) {
                        if (checkBoxList.get(i).isSelected()) {
                            selectedValues.add(values[i]);
                        }
                    }
                }
            } else {
                for (int i = 0; i < checkBoxList.size(); i++) {
                    if (checkBoxList.get(i).isSelected()) {
                        selectedValues.add(values[i]);
                    }
                }
            }

            return selectedValues.toArray(new Object[selectedValues.size()]);
        }


        @Override
        public void actionPerformed(ActionEvent arg0) {
            // TODO Auto-generated method stub
            Object source = arg0.getSource();
            if (source instanceof JButton) {
                JButton button = (JButton) source;
                if (button.equals(commitButton)) {
                    setText(getSelectedValues());
                    popup.setVisible(false);
                } else if (button.equals(cancelButton)) {
                    popup.setVisible(false);
                }
            }
        }

    }

    public static boolean haveSameCharacters(String str1, String str2) {
        Set<Character> set1 = new HashSet<>();
        Set<Character> set2 = new HashSet<>();

        for (char c : str1.toCharArray()) {
            set1.add(c);
        }

        for (char c : str2.toCharArray()) {
            set2.add(c);
        }

        // 判断两个集合是否有交集，如果有交集则说明有相同的字符
        return !Collections.disjoint(set1, set2);
    }
}
